#pragma once
//这个就是我们的日志小插件，以后要使用日志的文件就直接把这个mylog文件夹下的所有文件拷贝到目标文件路径下，并且我们已经在mylog文目录下创建了log目录用来作为创建log.txt等文件的载体目录

#include<iostream>
#include<string>
#include<cerrno>
#include<cstring>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<cstdlib>
#include<stdarg.h>
using std::cout;
using std::endl;
#define SIZE 1024
//完成一个日志的小插件，可以帮我们把当前进程的一些与运行与操作产生的信息或者错误给记录下来

//注意！！！：log目录要提前在当前路径下创建好，用来记录日志信息，否则不会帮我们创建log目录，后面的信息也全部丢失
//log.txt文件不存在我们就会创建，但是./log/路径是不会帮我们创建的，所以当前路径下一定要有log目录才能成功创建log.txt等日志文件

//日志等级：
#define Info  0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

//设置日志的输出目标：
#define Screen 1  //输出到屏幕
#define Onefile 2//输出到一个文件
#define Mulfile 3 //按照日志等级分为多个文件存储信息

#define LogFile "log.txt"

//写一个日志类
class Log
{
public:
    Log()
    {
        printMethod=Screen;
        path="./log/";//创建的路径我们默认设置为当前路径下的log目录然后再创建一个log.txt记录日志信息
        //注意！！！：log目录要提前在当前路径下创建好，否则不会帮我们创建log目录，后面的信息也全部丢失
    }
    //设置日志的打印方式
    void Enable(int method)
    {
        printMethod=method;
    }

    //将日志等级转为字符串类型
    std::string level_to_string(int level)
    {
        switch (level)
        {
        case Info: 
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal: 
            return "Fatal";
        default:
            return "None";
        }
    }

    //打印到一个文件的函数：
    void printOneFile(const std::string &logname,const std::string &logtxt)
    {
        std::string logname_=path+logname;//路径和文件名结合：./log/log.txt
        //使用open在指定路径下创建文件
        int fd=open(logname_.c_str(),O_WRONLY|O_APPEND|O_CREAT,0666);//open函数打开的文件字符串要是const char*类型的，logname_是string类型不符合，所以要使用c_str()返回logname_字符串的const char*类型
        if(fd<0)
        {
            return;
        }
        write(fd,logtxt.c_str(),logtxt.size());
        close(fd);

    }
    //多文件输出的函数：
    void printMulFile(int level,const std::string &logtxt)
    {
        std::string filename=LogFile;
        filename+=".";
        filename+=level_to_string(level);
        //到此打开的文件就是和等级有关的文件，例如：log.txt.Debug等类似的文件

        //再复用输入到单个文件的函数：
        printOneFile(filename,logtxt);//由于不同等级打开文件的文件名不同所以每次输出的内容也在不同的文件中，从而对日志信息进行了分类

    }


    //打印信息到日志：
    void printLog(int level,const std::string &logtxt)//需要传参等级和日志信息
    {
        switch(printMethod)
        {
        case Screen:
            cout<<logtxt<<endl;
            break;
        case Onefile:
            printOneFile(LogFile,logtxt);//调用打印到一个文件的函数
            break;
        case Mulfile:
            printMulFile(level,logtxt);//调用打印到多个文件的函数
            break;
        default:
            break;
        }
    }
    ~Log()
    {
        //没有要析构的数据
    }

    //重载一个()括号的输入日志信息的方式，让我们对日志的使用更加方便
    void operator()(int level,const char*format,...)
    {
        time_t t=time(nullptr);
        struct tm *ctime=localtime(&t);//将 time_t 类型的时间值 t 转换为 tm 结构体，表示当地时间。localtime 返回一个指向 tm 结构的指针，该结构包含了年、月、日、时、分、秒等信息。
        //就是把时间换算成文字信息保存在结构体中
        char leftbuffer[SIZE];
        char rightbuffer[SIZE];
        //把时间信息按照格式保存在字符数组中
        snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%d-%d-%d %d:%d:%d]",level_to_string(level).c_str(),ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,ctime->tm_hour,ctime->tm_min,ctime->tm_sec);

        va_list s;//定义了一个类型为 va_list 的变量 s，用于存储可变参数的列表。va_list 是一个结构体，定义在 stdarg.h 中，专门用于处理可变参数函数的参数。
        va_start(s,format);//这里是为了初始化一下列表s，format是我们可变参数列表的前一个参数，是为了确定我们的可变参数在传参中的位置，所以可变参数前面至少要有一个实际的参数
        //初始化之后。s中就存放了可变参数的所有值
        vsnprintf(rightbuffer,sizeof(rightbuffer),format,s);//专门给可变参数使用的，可以在可变参数列表s中按照format格式打印到目标数组中

        va_end(s);//使用start初始化可变列表后必须使用end（）释放start的资源

        //最后我们把左右字符串结合放在日志中：
        char logtxt[SIZE*2];
        snprintf(logtxt,sizeof(logtxt),"%s %s\n",leftbuffer,rightbuffer);

        //打印到目标日志文件中：
        printLog(level,logtxt);
        
    }

private:
    int printMethod;//打印方式
    std::string path;//日志路径
};

